import {
  Scene,
  Shape,
  Mesh,
  Group,
  BoxGeometry,
  MeshBasicMaterial,
  Vector3,
  ExtrudeGeometry,
  EdgesGeometry,
  Material,
  LineBasicMaterial,
  LineSegments,
  BufferGeometry,
  FrontSide,
  DoubleSide,
  ArrowHelper,
  Vector2,
  LineCurve3,
  CurvePath,
  Box3,
  GridHelper,
  type ColorRepresentation,
} from "three";
import {
  AssemblyProductGroup,
  Product3DGroup,
  Sub3DGroup,
  Base3DGroup,
} from "../types/DrawingGroups/groups";
import {
  Part3DMesh,
  Loft3DMesh,
  MouseSenseCube,
  Box3DMesh,
} from "../types/DrawingGroups/meshs";
import type {
  KujialeParamModel,
  KujialePath,
  KujialeResultLine,
} from "../types/KujialeJsonModels/KujialeParamModel";
import {
  BaseModel,
  LoftModel,
  PartModel,
  BoxModel,
  ProductModel,
  SubModel,
  type AssemblyProductModel,
} from "../types/Models";
import { DrawPart3DMesh } from "./part";
import { DrawLoft3DMesh } from "./loft";
import { Draw3DBoxMesh } from "./box3d";
import { type DrawContext } from "./context";
import { DrawMeshWireframeLines } from "./wireframe";

/**
 * 将已有产品排列成一条直线
 */
export function MoveProductsOnLine(scene: Scene) {
  const fstG = scene.children.find((it) => it instanceof Base3DGroup);
  if (fstG == undefined) {
    return;
  }
  let x = 0;
  const offsetX = fstG.position.x; //绘制偏移量，正常绘制时为了把图形尽可能靠近0点进行了偏移，导致和酷家乐的坐标有偏差
  const offsetZ = fstG.position.z;

  const groups = scene.children
    .filter((g) => g instanceof Base3DGroup)
    .forEach((g) => {
      //如果是组合，组合坐标一直都是000，无法移动和旋转
      if (g instanceof AssemblyProductGroup) {
        const model = g.model as AssemblyProductModel;

        //计算坐标轴旋转后的位置
        var vx = new Vector3(1, 0, 0);
        var vz = new Vector3(0, 0, 1);
        vx = vx.applyAxisAngle(
          new Vector3(0, 1, 0),
          model.kujialeAssemblyModel.rotate.z
        );
        vx.set(
          parseFloat(vx.x.toFixed(2)),
          parseFloat(vx.y.toFixed(2)),
          parseFloat(vx.z.toFixed(2))
        );
        vz = vz.applyAxisAngle(
          new Vector3(0, 1, 0),
          model.kujialeAssemblyModel.rotate.z
        );

        //计算移动和旋转原点，group内的元素以此点为原点
        var fstGx =
          model.kujialeAssemblyModel.position.x -
          (vx.x * model.kujialeAssemblyModel.size.x) / 2 -
          (vz.x * model.kujialeAssemblyModel.size.y) / 2;
        var fstGz =
          -model.kujialeAssemblyModel.position.y -
          (vx.z * model.kujialeAssemblyModel.size.x) / 2 -
          (vz.z * model.kujialeAssemblyModel.size.y) / 2;

        g.children
          //.filter((s) => s instanceof Base3DGroup)
          .forEach((s) => {
            var oldy = s.position.y;

            var vector = new Vector3(
              s.position.x - fstGx,
              0,
              s.position.z - fstGz
            );
            var newz =
              (vector.x * vx.z - vector.z * vx.x) / (vz.x * vx.z - vz.z * vx.x);
            var newx =
              vx.x == 0
                ? (vector.z - vz.z * newz) / vx.z
                : (vector.x - vz.x * newz) / vx.x;

            s.position.set(0, s.position.y, 0); //先移动回原点
            s.rotateOnWorldAxis(
              new Vector3(0, 1, 0),
              -model.kujialeAssemblyModel.rotate.z
            ); //并旋转
            s.position.set(
              x + newx - offsetX,
              oldy,
              s.position.z + newz - offsetZ
            );
          });

        x += model.kujialeAssemblyModel.size.x + 1000;
      } else {
        g.rotation.set(0, 0, 0);
        g.position.set(x + offsetX, g.position.y, 0 );
        const [model] = g.model.kujialeModel!;
        x += model.size!.x + 1000;
      }
    });

  reset_grids(scene);
}

export function reset_grids(scene: Scene) {
  var groups = scene.children.filter(
    (it) => it instanceof AssemblyProductGroup || it instanceof Product3DGroup
  );

  const grid_size = 500;
  var box = new Box3();
  groups.forEach((g) => {
    var pbox = new Box3();
    pbox.setFromObject(g);
    box.union(pbox);
  });

  var [grid] = scene.children.filter((it) => it instanceof GridHelper);
  if (grid) {
    var max_x =
      Math.abs(box.max.x) > Math.abs(box.min.x)
        ? Math.abs(box.max.x)
        : Math.abs(box.min.x);
    var max_z =
      Math.abs(box.max.z) > Math.abs(box.min.z)
        ? Math.abs(box.max.z)
        : Math.abs(box.min.z);
    var max = max_x > max_z ? max_x : max_z;
    var grid_num = (max * 2) / grid_size;

    scene.remove(grid);
    scene.add(new GridHelper(max * 2, grid_num));
  }
}

export function DrawAssemblyProductCube(
  amgroup: AssemblyProductGroup
): MouseSenseCube {
  var ammodel = amgroup.model as AssemblyProductModel;
  var json = ammodel.kujialeAssemblyModel;

  const boxmesh = new MouseSenseCube(
    new BoxGeometry(json.size.x, json.size.z, json.size.y),
    new MeshBasicMaterial({ color: "#ff0000", transparent: true, opacity: 0.1 })
  );

  (boxmesh.material as MeshBasicMaterial).depthTest = false; //总是显示，不会被遮挡
  boxmesh.visible = false;
  //绘制线框
  DrawMeshWireframeLines(boxmesh, false, 0xff0000);

  //移动到绝对位置
  boxmesh.rotateOnWorldAxis(new Vector3(1, 0, 0), json.rotate.x);
  boxmesh.rotateOnWorldAxis(new Vector3(0, 0, -1), json.rotate.y);
  boxmesh.rotateOnWorldAxis(new Vector3(0, 1, 0), json.rotate.z);
  boxmesh.position.add(
    new Vector3(json.position.x, json.position.z, -json.position.y)
  );
  return boxmesh;
}

export function DrawAssemblyProduct(
  amgroup: AssemblyProductGroup,
  context: DrawContext
): void {
  const boxmesh = DrawAssemblyProductCube(amgroup);
  amgroup.add(boxmesh);

  (amgroup.model.kujialeModel as KujialeParamModel[]).forEach((m) => {
    const product = new Product3DGroup(new ProductModel(m));
    DrawProductModelOnAbsPosition(product, context);
    amgroup.add(product);
  });
}

export function DrawProductModelOnAbsPosition(
  group: Product3DGroup,
  context: DrawContext
): void {
  DrawSubmodels(undefined, group, context);

  //移动到绝对位置
  var [kujialemodel] = group.model.kujialeModel!;
  group.rotateOnWorldAxis(new Vector3(1, 0, 0), kujialemodel.absRotation.x);
  group.rotateOnWorldAxis(new Vector3(0, 0, -1), kujialemodel.absRotation.y);
  group.rotateOnWorldAxis(new Vector3(0, 1, 0), kujialemodel.absRotation.z);
  group.position.add(
    new Vector3(
      kujialemodel.absPosition.x,
      kujialemodel.absPosition.z,
      -kujialemodel.absPosition.y
    )
  );
}

export function DrawModelCube(productGroup: Product3DGroup | Sub3DGroup) {
  var [jsonmodel] = productGroup.model.kujialeModel!;
  let w = jsonmodel.getModelWidth();
  let h = jsonmodel.getModelHeight();
  let d = jsonmodel.getModelDepth();
  if (w * h * d == 0) {
    console.log("size 0!", productGroup);
  }
  const boxmesh = new MouseSenseCube(
    new BoxGeometry(w, h, d),
    new MeshBasicMaterial({ color: "#000011", transparent: true, opacity: 0.1 })
  );
  boxmesh.position.add(new Vector3(w / 2, h / 2, d / 2));
  boxmesh.visible = false;
  //绘制线框
  DrawMeshWireframeLines(boxmesh, false, 0xff0000);

  productGroup.add(boxmesh); //加入到产品中
}

export function DrawSubmodels(
  parentGroupModel: BaseModel | undefined,
  group: Sub3DGroup | Product3DGroup,
  context: DrawContext
) {
  var [model] = group.model.kujialeModel!;
  if (model.size!.x * model.size!.y * model.size!.z == 0) {
    return; //如果尺寸有一个为0的，就不需要继续绘制，也无法绘制
  }

  var [jsonModel] = group.model.kujialeModel!;
  if (jsonModel.paramPlankPath) {
    //绘制板件
    let partMesh = DrawPart3DMesh(jsonModel, context);
    if (partMesh) {
      group.add(partMesh);
    }
  } else if (jsonModel.paramLoftPath) {
    const loftMesh = DrawLoft3DMesh(jsonModel, context);
    if (loftMesh) {
      group.add(loftMesh);
    }
  } else if (jsonModel.modelTypeId == 2) {
    const mesh = Draw3DBoxMesh(jsonModel, context);
    if (mesh) {
      group.add(mesh);
    }
  } else if (jsonModel.subModels) {
    //如果是元件不需要绘制外框
    DrawModelCube(group);
    //上面的if是元件，理论上元件不应该会有submodels，所以执行到这里时只剩下含有submodels的组件
    jsonModel.subModels.forEach((s) => {
      const subgroup = new Sub3DGroup(new SubModel(s));
      DrawSubmodels(group.model, subgroup, context);
      if (s.subModels) {
        //这个判断重要，否则元件层会被执行两次坐标旋转
        //把自己旋转+移动到相对位置上
        //不是板件才执行，否则和板件绘制中的旋转+位移重复+冲突了
        subgroup.rotateOnWorldAxis(new Vector3(1, 0, 0), s.rotate.x);
        subgroup.rotateOnWorldAxis(new Vector3(0, 0, -1), s.rotate.y);
        subgroup.rotateOnWorldAxis(new Vector3(0, 1, 0), s.rotate.z);
        subgroup.position.set(s.position.x, s.position.z, -s.position.y);
      }
      group.add(subgroup);
    });
  }
}

export function test_loft(scene: Scene) {
  const path_points = [new Vector3(0, 0, 0), new Vector3(0, 0, 258)];
  const shapepoints = [
    new Vector2(0, 0),
    new Vector2(16, 0),
    new Vector2(0, 16),
  ];
  const shape = new Shape();
  const [startPoint, ...otherPoints] = shapepoints;
  shape.moveTo(startPoint.x, startPoint.y);
  otherPoints.forEach((point, index) => {
    shape.lineTo(point.x, point.y);
  });

  //const geometry = new ExtrudeGeometry(shape, { depth: 300});//Z+方向
  //const curve = new LineCurve3(new Vector3(0, 0, 0), new Vector3(0, -500, 0));//y方向直线
  const curve = new CurvePath();
  curve.curves.push(
    new LineCurve3(new Vector3(0, 0, 0), new Vector3(200, 0, 0))
  );
  curve.curves.push(
    new LineCurve3(new Vector3(200, 0, 0), new Vector3(200, 100, 0))
  );
  curve.curves.push(
    new LineCurve3(new Vector3(200, 100, 0), new Vector3(0, 100, 0))
  );
  curve.curves.push(
    new LineCurve3(new Vector3(0, 100, 0), new Vector3(0, 0, 0))
  );
  const geometry = new ExtrudeGeometry(shape, {
    extrudePath: curve,
    bevelEnabled: false,
    steps: 300, //分段多？
  });
  var material = new MeshBasicMaterial({ color: "red" });
  var mesh = new Mesh(geometry, material);
  mesh.rotateOnWorldAxis(new Vector3(1, 0, 0), Math.PI / 2);
  //绘制线框
  DrawMeshWireframeLines(mesh, true, 0xffffff);
  scene.add(mesh);
}

export function ChangeAssemblyProductPartVisibleByTextureName(
  asProductGroup: AssemblyProductGroup,
  colorVisibleDict: { [key: string]: boolean }
) {
  asProductGroup.children.forEach((g) => {
    if (g instanceof Product3DGroup) {
      ChangeProductPartVisibleByTexture(g, colorVisibleDict);
    }
  });
}

export function ChangeProductPartVisibleByTexture(
  productGroup: Product3DGroup | Sub3DGroup,
  colorVisibleDict: { [key: string]: boolean }
) {
  productGroup.children.forEach((child) => {
    if (child instanceof Part3DMesh || child instanceof Loft3DMesh) {
      var [json] = child.model.kujialeModel!;
      if (json.textureName in colorVisibleDict) {
        child.visible = colorVisibleDict[json.textureName];
      }
    } else if (child instanceof Product3DGroup || child instanceof Sub3DGroup) {
      ChangeProductPartVisibleByTexture(child, colorVisibleDict);
    }
  });
}

export function ChangeAssemblyProductPartVisibleByCategory(
  asProductGroup: AssemblyProductGroup,
  cateVisibleDict: { [key: number]: boolean }
) {
  asProductGroup.children.forEach((g) => {
    if (g instanceof Product3DGroup) {
      ChangeProductPartVisibleByCategory(g, cateVisibleDict);
    }
  });
}

export function ChangeProductPartVisibleByCategory(
  productGroup: Product3DGroup | Sub3DGroup,
  cateVisibleDict: { [key: number]: boolean }
) {
  productGroup.children.forEach((child) => {
    if (child instanceof Product3DGroup || child instanceof Sub3DGroup) {
      var [json] = child.model.kujialeModel!;

      //正向，只控制规定组件的显示/不显示，不是字典里的组件不控制
      if (json.prodCatId in cateVisibleDict) {
        child.visible = cateVisibleDict[json.prodCatId];
      } else {
        ChangeProductPartVisibleByCategory(child, cateVisibleDict);
      }
    }
  });
}

export function offsetToCenter(scene: Scene) {
  var groups = scene.children.filter(
    (it) => it instanceof AssemblyProductGroup || it instanceof Product3DGroup
  );

  var box = new Box3();
  groups.forEach((g) => {
    var pbox = new Box3();
    pbox.setFromObject(g);
    box.union(pbox);
  });

  var center = new Vector3();
  box.getCenter(center);

  groups.forEach((it) => it.position.add(new Vector3(-center.x, 0, -center.z)));
}
